/**
 * RefundServiceImp.java
 * 
 * Copyright@2015 OVT Inc. All rights reserved. 
 * 
 * 2015年12月22日
 */
package com.ovt.order.service;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.ovt.common.exception.OVTRuntimeException;
import com.ovt.common.utils.CollectionUtils;
import com.ovt.common.utils.DataConvertUtils;
import com.ovt.common.utils.NumberGeneratorUtil;
import com.ovt.common.utils.StringUtils;
import com.ovt.order.dao.PaymentDao;
import com.ovt.order.dao.RefundDao;
import com.ovt.order.dao.contant.OrderState;
import com.ovt.order.dao.contant.DBConstants.TABLES.ALI_REFUND_LOG;
import com.ovt.order.dao.vo.Refund;
import com.ovt.order.dao.vo.RefundRequest;
import com.ovt.order.service.alipay.AlipayConfig;
import com.ovt.order.service.alipay.AlipaySubmit;
import com.ovt.order.service.alipay.HttpRequest;
import com.ovt.order.service.alipay.AlipayConstants.AlipayRefund;
import com.ovt.order.service.exception.ServiceErrorCode;
import com.ovt.order.service.exception.ServiceException;

/**
 * RefundServiceImp
 * 
 * @Author leo.liu
 * @Version 1.0
 * @See
 * @Since [OVT Cloud Platform]/[API] 1.0
 */
@Service
public class RefundServiceImp implements RefundService
{
    @Autowired
    private RefundDao refundDao;

    @Autowired
    private PaymentDao paymentDao;

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.RefundService#doAliRefund(java.util.List)
     */
    @Override
    public String doAliRefund(List<RefundRequest> requests)
            throws ServiceException
    {
        String batchNo = NumberGeneratorUtil.generateRefundBatchNo();

        List<Refund> refundList = createRefunds(requests, batchNo);

        String detailData = generateAlipayDetailData(refundList);

        Map<String, String> sParaTemp = new HashMap<String, String>();
        sParaTemp.put(AlipayRefund.PARAM_NAME_SERVICE,
                AlipayRefund.SERVICE_NAME);
        sParaTemp.put(AlipayRefund.PARAM_NAME_PARTNER, AlipayConfig.partner);
        sParaTemp.put(AlipayRefund.PARAM_NAME_INPUT_CHARSET,
                AlipayConfig.input_charset);
        sParaTemp.put(AlipayRefund.PARAM_NAME_NOTIFY_URL,
                AlipayConfig.refund_notify_url);
        sParaTemp.put(AlipayRefund.PARAM_NAME_SELLER_EMAIL,
                AlipayConfig.seller_email);
        sParaTemp.put(AlipayRefund.PARAM_NAME_REFUND_DATE, DataConvertUtils
                .toString(new Timestamp(System.currentTimeMillis())));
        sParaTemp.put(AlipayRefund.PARAM_NAME_BATCH_NO, batchNo);
        sParaTemp.put(AlipayRefund.PARAM_NAME_BATCH_NUM,
                ((Integer) refundList.size()).toString());
        sParaTemp.put(AlipayRefund.PARAM_NAME_DETAIL_DATA, detailData);

        String sHtmlText = AlipaySubmit.buildRequest(sParaTemp,
                HttpRequest.METHOD_GET, AlipayRefund.REFUND_CONFIRM_PAGE_OK);

        return sHtmlText;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.RefundService#updateRefunds(java.util.Map)
     */
    @Override
    public void updateRefunds(List<Refund> refunds) throws ServiceException
    {
        if (CollectionUtils.isNotEmpty(refunds))
        {
            try
            {
                refundDao.update(refunds);
            }
            catch (OVTRuntimeException e)
            {
                throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                        "Failed to update refund list!", e);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.RefundService#getRefund(long)
     */
    @Override
    public Refund getRefund(long refundRequestId) throws ServiceException
    {
        Refund refund = null;
        try
        {
            refund = refundDao.getRefund(refundRequestId);
        }
        catch (OVTRuntimeException e)
        {
            throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                    "Failed to get refund by refund request id!", e);
        }

        return refund;
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.ovt.service.RefundService#getRefundList()
     */
    @Override
    public List<Refund> getRefundList() throws ServiceException
    {
        List<Refund> refundList = new ArrayList<Refund>();
        try
        {
            refundList = refundDao.getRefundList();
        }
        catch (OVTRuntimeException e)
        {
            throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                    "Failed to get refund list!", e);
        }

        return refundList;
    }

    public List<Long> getRefundRequestIdsByBatchNo(String batchNo)
            throws ServiceException
    {
        List<Long> ids = null;
        try
        {
            ids = refundDao.getRefundRequestIdsByBatchNo(batchNo);
        }
        catch (OVTRuntimeException e)
        {
            throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                    "Failed to get refund request ids by batch no!", e);
        }
        
        return ids;
    }

    private Map<String, String> getOrderTradeMap(List<RefundRequest> requests)
            throws ServiceException
    {
        Map<String, String> orderTradeMap = new HashMap<String, String>();

        List<String> orderNoList = new ArrayList<String>();
        for (RefundRequest request : requests)
        {
            orderNoList.add(request.getOrderNo());
        }

        try
        {
            orderTradeMap = paymentDao.getTradeNoByOrderNo(orderNoList);
        }
        catch (OVTRuntimeException e)
        {
            throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                    "Failed to get order trade no map!", e);
        }

        return orderTradeMap;
    }

    private List<Refund> createRefunds(List<RefundRequest> requests,
            String bacthNo) throws ServiceException
    {
        List<Refund> refundList = new ArrayList<Refund>();
        if (CollectionUtils.isNotEmpty(requests))
        {
            Map<String, String> orderTradeMap = getOrderTradeMap(requests);
            for (RefundRequest request : requests)
            {
                Refund refund = new Refund();
                refund.setRefundRequestId(request.getId());
                refund.setBatchNo(bacthNo);
                refund.setTradeNo(orderTradeMap.get(request.getOrderNo()));
                refund.setRefundState(OrderState.ALIPAY_REFUND_APPLY);
                refund.setRefundFee(request.getRefundFee());

                refundList.add(refund);
            }

            try
            {
                refundDao.save(refundList);
            }
            catch (OVTRuntimeException e)
            {
                throw new ServiceException(ServiceErrorCode.DB_SERVER_ERROR,
                        "Failed to save refund list!", e);
            }
        }

        return refundList;
    }

    private String generateAlipayDetailData(List<Refund> refundList)
    {
        StringBuffer detailData = new StringBuffer();
        if (CollectionUtils.isNotEmpty(refundList))
        {
            for (Refund refund : refundList)
            {
                detailData.append(refund.getTradeNo())
                        .append(StringUtils.CARET)
                        .append(refund.getRefundFee())
                        .append(StringUtils.CARET)
                        .append(AlipayRefund.REFUND_REASON)
                        .append(StringUtils.HASH);
            }

            detailData.deleteCharAt(detailData.length() - 1);
        }

        return detailData.toString();
    }

    public List<Refund> parseAlipayRefundNotifyParams(Map<String, String> params)
    {
        List<Refund> refundList = new ArrayList<Refund>();

        String batchNo = params.get(ALI_REFUND_LOG.BATCH_NO);
        String detailData = params.get(ALI_REFUND_LOG.RESULT_DETAILS);

        if (StringUtils.isBlank(batchNo) || StringUtils.isBlank(detailData))
        {
            return null;
        }

        String[] refundDetails = detailData.split(StringUtils.HASH);
        if (null != refundDetails)
        {
            for (int i = 0; i < refundDetails.length; i++)
            {
                boolean hasTaxFlag = false;
                String refundDetail = refundDetails[i];
                if (refundDetail.contains(StringUtils.DOLLAR))
                {
                    hasTaxFlag = true;
                    refundDetail = refundDetail.replace(StringUtils.DOLLAR,
                            StringUtils.BLANK);
                }

                String[] refundDetailInfos = refundDetail
                        .split(StringUtils.CARET);
                if (null != refundDetailInfos)
                {
                    Refund refund = new Refund();
                    refund.setBatchNo(batchNo);
                    refund.setTradeNo(refundDetailInfos[0]);
                    refund.setRefundFee(Float.valueOf(refundDetailInfos[1]));
                    if (refundDetailInfos[2].equals(StringUtils.SUCCESS))
                    {
                        refund.setRefundState(OrderState.ALIPAY_REFUND_SUCCESS);
                    }
                    else
                    {
                        refund.setRefundState(OrderState.ALIPAY_REFUND_FAILED);
                    }

                    if (hasTaxFlag)
                    {
                        refund.setRefundTax(Float.valueOf(refundDetailInfos[5]));
                        refund.setRefundTaxState(refundDetailInfos[2]);
                    }
                    refundList.add(refund);
                }
            }
        }

        return refundList;
    }
}
